/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package Control;

import Model.TableLexema;
import Model.Token;
import Model.symtable.EntryClass;
import Model.symtable.EntryMethod;
import Model.symtable.EntryVar;
import Model.symtable.Symtable;
import Model.symtable.Tipo;
import com.sun.org.apache.xerces.internal.util.SymbolTable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Alan Carlos
 */
public class Gramatica {

    Parser parser;
    //private Collection<String> firstTipo;
    private ArrayList<Token> listToken;
    private Symtable tabelaSimbolos;
    private EntryClass classe;
    private EntryVar variavel;
    private EntryMethod metodo;
    private boolean global;
    private boolean blocoMetodo;
    private boolean blocoParametros;
    //variavel criada para controlar o tipo dos operandos quando não estão em uma atribuição
    private boolean firstID_SemAtribuicao;
    private boolean blocoClasse;
    private boolean sinalComparar;
    private int controleDimensaoVetor;// variavel para comparar dimensão da variavel acessada é compativel com a variavel declarada
    private int controleAcessoVetor;
    private boolean indiceVetor;
    private boolean construtor;
    private boolean blocoChamadaMetodo;
    private boolean blocoDeclaracaoVariaveis;
    private EntryMethod metodoChamado;

    //Iterator<Token> itToken; 
    public Gramatica(Parser parser, Symtable tabelaSimbolos) {
        this.parser = parser;
        listToken = parser.getListToken();
        this.tabelaSimbolos = tabelaSimbolos;
        this.global = true;
        this.blocoMetodo = false;
        this.blocoParametros = false;
        this.firstID_SemAtribuicao = false;
        this.blocoClasse = false;

    }

    //<Inicio> ::=  <definicao_constante> <definicao_variaveis> <auxiliar_declaracao_classe>
//<auxiliar_declaracao_classe> ::= <declaracao_classe> <auxiliar_declaracao_classe> | :
// -------------INÍCIO --------------------------------
    public void inicio(String follow) {
        definicaoConstante("{:}:constants:variables:class");
        definicaoVariaveis("{:}:constants:variables:class");
        global = false;
        auxiliarDeclaracaoClasse("}");
        if(!tabelaSimbolos.hasMain()){
            SemanticError("Falta ser declado o método main");
        }
    }

    public void auxiliarDeclaracaoClasse(String follow) {
        if (parser.getCurrentToken().getLexema().equals("class")) {
            blocoClasse = true;
            declaracaoClasse(follow);
            blocoClasse = false;
            auxiliarDeclaracaoClasse(follow);
        }
    }
// -------------FIM INCÍCIO ---------------------------
// ------------ VETOR ----------------------------------

    public void vetor(String follow) {
        if (parser.getCurrentToken().getLexema().equals("[")) {
            parser.nextToken();
            vetorAux("*:/:+:-:)");
        }
        else{
            if(blocoDeclaracaoVariaveis){
                variavel.setDim(0);                
            }
            else{
                controleAcessoVetor = 0;
            }
                }
    }

    public void vetorAux(String follow) {
        try {
            //ANÁLISE SINTÁTICA
            this.indiceVetor = true;
            this.firstID_SemAtribuicao = true;
            EntryVar varAux = variavel;
            
            expressaoAritmetica("]");
            variavel = varAux;
            this.firstID_SemAtribuicao = false;
            this.indiceVetor = false;
            //FIM DA ANÁLISE SINTÁTICA
            if (parser.getCurrentToken().getLexema().equals("]")) {
                //configurando dimensão da variavel atual para vetor
            if(blocoDeclaracaoVariaveis){
                variavel.setDim(1);
                
            }
            else{
                controleAcessoVetor = 1;
            }
                parser.nextToken();
                vetorAux2(follow);
                
            } else {
                SintaticError("Esperado ]");
            }

        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático em Vetor", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void vetorAux2(String follow) {
        if (parser.getCurrentToken().getLexema().equals("[")) {
            parser.nextToken();
            vetorAux3(follow);
        }

    }

    public void vetorAux3(String follow) {
        try {
            this.indiceVetor = true;
            this.firstID_SemAtribuicao = true;
            EntryVar varAux = variavel;
            expressaoAritmetica("]");
            variavel = varAux;
            this.firstID_SemAtribuicao = false;
            this.indiceVetor = false;
            if (parser.getCurrentToken().getLexema().equals("]")) {
                if(blocoDeclaracaoVariaveis){
                variavel.setDim(2);
                
            }
            else{
                controleAcessoVetor = 2;
            }
                //modificando a dimensão da variavel atual de vetor para matriz 
                parser.nextToken();
            } else {
                SintaticError("Esperado ]");
            }
            

        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático em Vetor", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }
// ------------ FIM VETOR ---------------------------------- 

// ------------ INSTRUCAO ---------------------------------- 
    public void instrucao(String follow) {
        if (checkInstrucaoGeral()) {
            instrucaoGeral(follow.concat(":;"));
            if (parser.getCurrentToken().getLexema().equals(";")) { //alternativa para se recuperar do erro de ;
                parser.nextToken();
            }
            instrucao(follow);
        }

    }

    public void instrucaoGeral(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals("for")) {
                comandoFor(follow);

            } else if (parser.getCurrentToken().getLexema().equals("if")) {
                comandoIf(follow);

            } else if (parser.getCurrentToken().getLexema().equals("read")) {
                read(";");
                if (parser.getCurrentToken().getLexema().equals(";")) {
                    parser.nextToken();
                } else {
                    SintaticError("Esperado ;");
                }
            } else if (parser.getCurrentToken().getLexema().equals("write")) {
                write(";");
                if (parser.getCurrentToken().getLexema().equals(";")) {
                    parser.nextToken();
                } else {
                    SintaticError("Esperado ;");
                }
            } else if (parser.getCurrentToken().getLexema().equals("while")) {
                comandoWhile(";");
            } else if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) { //atribuição,inicializa_objeto,atributo_objeto, chamada_metodo
                Token tokenAux;
                int index = listToken.indexOf(parser.getCurrentToken());
                index++;
                tokenAux = listToken.get(index);
                if (tokenAux.getLexema().equals("=")) {
                    atribuicao(";");
                    if (parser.getCurrentToken().getLexema().equals(";")) {
                        parser.nextToken();
                    } else {
                        SintaticError("Esperado ;");
                    }
                } else if (tokenAux.getLexema().equals("++") || tokenAux.getLexema().equals("--")) {
                    inc_dec(follow);
                    if (parser.getCurrentToken().getLexema().equals(";")) {
                        parser.nextToken();
                    } else {
                        SintaticError("Esperado ;");
                    }
                    /* } else if (tokenAux.getTipo() == Token.IDENTIFICADOR) {
                     inicializaObjeto(";");
                     if (parser.getCurrentToken().getLexema().equals(";")) {
                     parser.nextToken();
                     } else {
                     SintaticError("Esperado ;");
                     }*/
                } else if (tokenAux.getLexema().equals("(")) {
                    chamadaMetodo(";");
                    if (parser.getCurrentToken().getLexema().equals(";")) {
                        parser.nextToken();
                    } else {
                        SintaticError("Esperado ;");
                    }
                } else if (tokenAux.getLexema().equals(".")) {
                    index++;
                    tokenAux = listToken.get(index);
                    if (tokenAux.getTipo() == Token.STRING || tokenAux.getTipo() == Token.NUMERO
                            || tokenAux.getTipo() == Token.CARACTERE) {
                        chamadaMetodo(";");
                        if (parser.getCurrentToken().getLexema().equals(";")) {
                            parser.nextToken();
                        } else {
                            SintaticError("Esperado ;");
                        }
                    } else if (tokenAux.getTipo() == Token.IDENTIFICADOR) {
                        index++;
                        tokenAux = listToken.get(index);
                        if (tokenAux.getLexema().equals("(")) {
                            chamadaMetodo(";");
                            if (parser.getCurrentToken().getLexema().equals(";")) {
                                parser.nextToken();
                            } else {
                                SintaticError("Esperado ;");
                            }
                        } else {
                            atributoObjeto(";");
                            if (parser.getCurrentToken().getLexema().equals(";")) {
                                parser.nextToken();
                            } else {

                                SintaticError("Esperado ;");
                            }
                        }
                    }
                } else if (tokenAux.getTipo() == Token.IDENTIFICADOR) {
                    SintaticError("Declaração deve ser feita nos blocos apropiados");
                }
                else if(tokenAux.getLexema().equals("[")){
                    SintaticError("Não e permitida atribuição para vetores");
                }
            } else {
                SintaticError("Esperado uma instrução");

            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático em Inst. Geral", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public boolean checkInstrucaoGeral() {
        Token currentToken = parser.getCurrentToken();
        String lexema = currentToken.getLexema();
        if (currentToken.getTipo() == Token.IDENTIFICADOR
                || currentToken.getTipo() == Token.NUMERO
                || lexema.equals("(")
                || lexema.equals("true")
                || lexema.equals("false")
                || lexema.equals("for")
                || lexema.equals("if")
                || lexema.equals("read")
                || lexema.equals("write")
                || lexema.equals("while")
                || checkTipo()) {
            return true;
        } else {
            return false;
        }
    }
// ------------ FIM DE INSTRUCAO ----------------------------------
// ------------ EXPRESSÃO ARITIMETICA ----------------------------------

    public void expressaoAritmetica(String follow) {
        termoArit(follow.concat(":+:-:)"));
        expressaAux(follow.concat(":)"));
    }

    //First(expressaoAritmetica){numero,identificador,(}
    public void expressaAux(String follow) {

        if (parser.getCurrentToken().getLexema().equals("+")) {
            parser.nextToken();
            termoArit(follow.concat(":+:-:)"));
            expressaAux(follow.concat(":)"));
        } else if (parser.getCurrentToken().getLexema().equals("-")) {
            parser.nextToken();
            termoArit(follow.concat(":+:-:)"));
            expressaAux(follow.concat(":)"));
        } else {
            //nada pra fazer
        }

    }

    //First(termoArit)={numero,identificador,(}
    public void termoArit(String follow) {
        fatorArit(follow.concat(":*:/:+:-:)"));
        expressaoAux2(follow.concat(":+:-:)"));

    }

    public void expressaoAux2(String follow) {
        if (parser.getCurrentToken().getLexema().equals("*")) {
            parser.nextToken();
            termoArit(follow.concat(":+:-:)"));
            expressaoAux2(follow.concat(":+:-:)"));
        } else if (parser.getCurrentToken().getLexema().equals("/")) {
            parser.nextToken();
            termoArit(follow.concat(":+:-:)"));
            expressaoAux2(follow.concat(":+:-:)"));
        } else {
            //SintaticError("Esperado + ou -");
        }

    }

    public void fatorArit(String follow) {
        try {
            if (parser.getCurrentToken().getTipo() == Token.NUMERO) {
                String numero = parser.getCurrentToken().getLexema();
                if (this.firstID_SemAtribuicao == true) { // primeira variável da expressão pra ser declarada.
                    if (numero.contains(".")) {
                        variavel = new EntryVar(numero, tabelaSimbolos.getClass(Symtable.FLOAT), 0, true); //cria variavel do tipo float para ser comparada
                    } else {
                        variavel = new EntryVar(numero, tabelaSimbolos.getClass(Symtable.INTEGER), 0, true); //cria variavel do tipo float para ser comparada
                    }
                    this.firstID_SemAtribuicao = false;
                }
                if (variavel.getType() != null) {
                    if (numero.contains(".")) {
                        if (this.indiceVetor == true) {
                            /*
                            Controle feito nos indices do vetor, pois  para acessar um posição em um vetor
                            pode-se usar uma expressão aritmetica, portanto caso alguns dos operandos
                            seja float no acesso à posição do vetor, tem que exibir erro.
                            */
                           
                            SemanticError("Permitido somente 'integer'");
                        }
                        if (!variavel.getType().getName().equals(Symtable.FLOAT)) {
                            SemanticError("Atribuição de tipos diferentes");
                        }
                    } else if (!variavel.getType().getName().equals(Symtable.INTEGER)) {
                        SemanticError("Atribuição de tipos diferentes");
                    }
                } else {
                    SemanticError("Atribuição de tipos diferentes");
                }
                parser.nextToken();
            } else if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                //análise semântica
                if (this.firstID_SemAtribuicao == true) { // primeira variável da expressão a ser usada.
                    variavel = tabelaSimbolos.contain(parser.getCurrentToken().getLexema(), classe, metodo);
                    if (variavel == null) {
                        variavel = new EntryVar(parser.getCurrentToken().getLexema(), null, 0, false);
                    }
                    
                    if (this.indiceVetor == true) {
                        if (!variavel.getType().getName().equals(Symtable.INTEGER)) { //verifica se dentro do vetor é um integer
                            SemanticError("Permitido somente 'integer'");
                        }
                    }
                    this.firstID_SemAtribuicao = false;
                }
                
                
                EntryVar varAux = tabelaSimbolos.contain(parser.getCurrentToken().getLexema(), classe, metodo);
                if (varAux == null) {
                    SemanticError("Identificador não declarado");
                } else if (variavel.getType() == null) {
                    SemanticError("identificadores de tipos diferentes");
                } else if (!varAux.getType().getName().equals(variavel.getType().getName())) {
                    SemanticError("identificadores de tipos diferentes");
                } 
                //fim da análise semântica
                
                controleDimensaoVetor = varAux.getDim();
                parser.nextToken();
                vetor(follow.concat(":*:/:+:-:)"));
                //controle de dimensão no acesso à vetores
                if(controleAcessoVetor!=controleDimensaoVetor){
                    SemanticError("Dimensão da variavel acessada difere da que foi declarada");}
            } else if (parser.getCurrentToken().getLexema().equals("(")) {
                parser.nextToken();
                expressaoAritmetica(follow.concat(":)"));
                if (parser.getCurrentToken().getLexema().equals(")")) {
                    parser.nextToken();
                } else {
                    SintaticError("Esperado )");
                }
            } else {
                SintaticError("Esperado Número, Identificador ou (");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático em exp. arit", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }
// ------------ FIM EXPRESSÃO ARITIMETICA ----------------------------------
//***************************************************************************
//----------------EXPRESSÃO LOGICA -------------------------------------

    public boolean isLogica(String follow) {

        boolean teste = false;
        Collection operator = new ArrayList();
        operator.add("true");
        operator.add("false");
        operator.add("&&");
        operator.add("||");

        int size = TableLexema.getInstance().getArray().size();
        int a = parser.getCurrentToken().getIndice();
        while ((a < size) && !(TableLexema.getInstance().getArray().get(a).getLexema().equals(";")) && !(TableLexema.getInstance().getArray().get(a).getLexema().equals(")"))) {
            if (operator.contains(TableLexema.getInstance().getArray().get(a).getLexema())) {
                teste = true;
            }
            a++;
            //saida do laço quando encontrar o operador
            if (teste) {
                break;
            }
        }

        return teste;
    }

    public void expressaoLogica(String follow) {
        termoLogico("||:)");
        expressaoLogicaAux(")");
    }

    public void expressaoLogicaAux(String follow) {
        if (parser.getCurrentToken().getLexema().equals("||")) {
            parser.nextToken();
            termoLogico("||:)");
        }
    }

    public void termoLogico(String follow) {
        fatorLogico("&&:||:)");
        termoLogicoAux("||:)");
    }

    public void termoLogicoAux(String follow) {
        if (parser.getCurrentToken().getLexema().equals("&&")) {
            parser.nextToken();
            fatorLogico("&&:||:)");
        }
    }

    public void fatorLogico(String follow) {
        try {
            if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                //análise semântica
                EntryVar varAux = tabelaSimbolos.contain(parser.getCurrentToken().getLexema(), classe, metodo);
                if (varAux == null) {
                    SemanticError("Identificador não declarado");
                }                
                else if (varAux.getType() == null) {
                    SemanticError("Identificador sem tipo definido");
                } else if (!varAux.getType().getName().equals(Symtable.BOOLEAN)) {
                    SemanticError("Identificador deve ser do tipo boolean");
                }
                if(varAux!=null)
                 controleDimensaoVetor=varAux.getDim();
                //fim da análise semântica
                parser.nextToken();
                vetor(follow);
                //controle de dimensão no acesso à vetores
                if(controleAcessoVetor!=controleDimensaoVetor)
                    SemanticError("Dimensão da variavel acessada difere da que foi declarada");
            } else if (parser.getCurrentToken().getLexema().equals("(")) {
                parser.nextToken();
                expressaoLogica(")");
                if (parser.getCurrentToken().getLexema().equals(")")) {
                    parser.nextToken();
                } else {
                    SintaticError("Esperado )");
                }
            } else if (parser.getCurrentToken().getLexema().equals("true")) {
                parser.nextToken();
            } else if (parser.getCurrentToken().getLexema().equals("false")) {
                parser.nextToken();
            } else {
                SintaticError("Esperado Identificador, (, true ou false");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático em exp. logica", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }

    }
//----------------FIM EXPRESSÃO LOGICA ---------------------------------  
//***************************************************************************
// ------------ EXPRESSAO RELACIONAL ---------------------------------------

    public boolean isRelacional(String follow) {
        boolean teste = false;
        Collection operator = new ArrayList();
        operator.add("!=");
        operator.add("==");
        operator.add(">");
        operator.add("<");
        operator.add(">=");
        operator.add("<=");
        int size = TableLexema.getInstance().getArray().size();
        int a = parser.getCurrentToken().getIndice();
        while ((a < size) && !(TableLexema.getInstance().getArray().get(a).getLexema().equals(";")) && !(TableLexema.getInstance().getArray().get(a).getLexema().equals(")"))) {
            if (operator.contains(TableLexema.getInstance().getArray().get(a).getLexema())) {
                teste = true;
            }
            a++;
            //saida do laço quando encontrar o operador
            if (teste) {
                break;
            }
        }

        return teste;
    }

    public void expressaoRelacional(String follow) {
        try {
            if (parser.getCurrentToken().getTipo() == Token.NUMERO
                    || parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR
                    || parser.getCurrentToken().getLexema().equals("(")) {//verifica se faz parte do conjunto First(expressao_aritmetica)
                expressaoAritmetica("<:>:>=:<=:==:!=");
                operadorRelacional();//follow{numero,identificador,(}
                relacionalAux(follow);
            } else if (parser.getCurrentToken().getTipo() == Token.STRING
                    || parser.getCurrentToken().getTipo() == Token.CARACTERE) { //verifica se faz parte do conjunto First(valor)
                valor("<:>:>=:<=:==:!=");
                operadorRelacional();//follow{numero,identificador,(}
                relacionalAux(follow);
            } else {
                SintaticError("Esperado Número, Identificador, String, Caractere ou (");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático em exp. relacional", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void relacionalAux(String follow) {
        try {
            if (parser.getCurrentToken().getTipo() == Token.NUMERO
                    || parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR
                    || parser.getCurrentToken().getLexema().equals("(")) { //verifica se faz parte do conjunto First(expressao_aritmetica)
                termoArit("+:-");
                expressaAux(follow);
            } else if (parser.getCurrentToken().getTipo() == Token.STRING
                    || parser.getCurrentToken().getTipo() == Token.CARACTERE) { //verifica se faz parte do conjunto First(valor)
                valor(follow);
            } else {
                SintaticError("Esperado Número, Identificador, String, Caractere ou (");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático em exp. relacional", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    /* public void relacionalAux2(String follow) {

     }
     public void relacionalAux3(String follow) {

     }

     public void relacionalAux_(String follow) {
     expressaAux(follow);
     }*/
    public void operadorRelacional() {

        try {
            if (parser.getCurrentToken().getLexema().equals(">")) {
                this.sinalComparar = false;
                parser.nextToken();
            } else if (parser.getCurrentToken().getLexema().equals("<")) {
                this.sinalComparar = false;
                parser.nextToken();
            } else if (parser.getCurrentToken().getLexema().equals(">=")) {
                this.sinalComparar = false;
                parser.nextToken();
            } else if (parser.getCurrentToken().getLexema().equals("<=")) {
                this.sinalComparar = false;
                parser.nextToken();
            } else if (parser.getCurrentToken().getLexema().equals("==")) {
                this.sinalComparar = true;
                parser.nextToken();
            } else if (parser.getCurrentToken().getLexema().equals("!=")) {
                this.sinalComparar = true;
                parser.nextToken();
            } else {
                SintaticError("Esperado >, <, >=, <=, == ou !=");
            }
            if (!this.sinalComparar) { //se for >, <, >=, <= e não for inteiro ou float exibe erro semântico.
                if (variavel.getType() != null) {
                    if (!(variavel.getType().getName().equals(Symtable.INTEGER) || variavel.getType().getName().equals(Symtable.FLOAT))) {
                        SemanticError(">, <, >= e <= só pode fazer operação em integer e float");
                    }
                } else {
                    SemanticError("Identificador não declarado");
                }
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no operador relacional", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());

            System.err.println(ex.getMessage());
            while (!(parser.getCurrentToken().getTipo() == Token.NUMERO
                    || parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR
                    || parser.getCurrentToken().getTipo() == Token.STRING
                    || parser.getCurrentToken().getTipo() == Token.CARACTERE
                    || parser.getCurrentToken().getLexema().equals(")"))) {
                if (parser.getCurrentToken().getLexema().equals("EOF")) {
                    break;
                }
                parser.nextToken();
            }
        }

    }

    public void operadorRelacionalIgualdade(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals("==")) {
                parser.nextToken();
            } else if (parser.getCurrentToken().getLexema().equals("!=")) {
                parser.nextToken();
            } else {
                SintaticError(follow);
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático em operador realcional", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

//----------------FIM EXPRESSÃO RELACIONAL ---------------------------------      
// ------------ COMANDO READ -----------------------------------------------
    public void read(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals("read")) {
                parser.nextToken();
                if (parser.getCurrentToken().getLexema().equals("(")) {
                    parser.nextToken();
                    fatorLeia(")");
                    if (parser.getCurrentToken().getLexema().equals(")")) {
                        parser.nextToken();
                        //System.out.println("Done read");
                    } else {
                        SintaticError("Esperado )");
                    }
                }
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no comando read", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void fatorLeia(String follow) {
        try {
            if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                    //controle para conferir se a variavel ja foi declarada
                    variavel = tabelaSimbolos.contain(parser.getCurrentToken().getLexema(), classe, metodo);
                    if (variavel == null) {
                    SemanticError("Identificador não declarado");
                    variavel = new EntryVar(false);
                    variavel.setName(parser.getCurrentToken().getLexema());
                    variavel.setDim(0);
                    variavel.setType(null);
                    }
                
                parser.nextToken();
                fatorLeiaAux(")");
            } else {
                SintaticError("Esperado um identificador");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no comando read", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void fatorLeiaAux(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals(",")) {
                parser.nextToken();
                if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                    parser.nextToken();
                    fatorLeiaAux(")");
                } else {
                    SintaticError("Esperado um identificador");
                }
            } else {

            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no comando read", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    // ------------ FIM COMANDO READ ------------------------------------
    // ------------ COMANDO WRITE ---------------------------------------
    public void write(String follow) {
        try {
            
            if (parser.getCurrentToken().getLexema().equals("write")) {
                parser.nextToken();
                
                if (parser.getCurrentToken().getLexema().equals("(")) {
                    parser.nextToken();
                    fatorWrite(")");
                    if (parser.getCurrentToken().getLexema().equals(")")) {
                        //retorna a variavel a seu valor padrão
                        firstID_SemAtribuicao=false;
                        parser.nextToken();
                        //System.out.println("Done write");
                    } else {
                        SintaticError("Esperado )");
                    }
                } else {
                    SintaticError("Esperado (");
                }
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no comando write", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

//TEM QUE INCLUIR A EXPRESSÃO ARITMETICA NAS POSSIBILIDADES
    public void fatorWrite(String follow) {
       
        try {
            //variavel global para controlar se alguma derivação levou ao comando write
            firstID_SemAtribuicao = true;
            if (parser.getCurrentToken().getTipo() == Token.NUMERO ||  parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR||parser.getCurrentToken().getLexema().equals(")") ) {
                
                expressaoAritmetica(follow);
                auxWrite(follow);
            } else if (parser.getCurrentToken().getTipo() == Token.STRING) {
                parser.nextToken();
                auxWrite(")");
            } else {
                SintaticError("Esperado um identificador, string ou número");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no comando write", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }
//TEM QUE INCLUIR A EXPRESSÃO ARITMETICA NAS POSSIBILIDADES

    public void auxWrite(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals(",")) {
                parser.nextToken();
                if (parser.getCurrentToken().getTipo() == Token.NUMERO || parser.getCurrentToken().getLexema().equals("(") || parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                    expressaoAritmetica(follow);
                    auxWrite(")");
                } else if (parser.getCurrentToken().getTipo() == Token.STRING) {
                    parser.nextToken();
                    auxWrite(")");
                } else {
                    SintaticError("Esperado um identificador, número ou string");
                }
            } else {

            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no comando write", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }
// ------------ FIM COMANDO WRITE ---------------------------------------

// ------------ COMANDO IF ---------------------------------
    public void comandoIf(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals("if")) {
                parser.nextToken();
                comandoIf2(follow.concat(":then"));
            } else {
                SintaticError("Esperado um If");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no comando if", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }

    }

    public void comandoIf2(String follow) {
        try {
            this.firstID_SemAtribuicao = true;
            //PRECISA ADICIONAR RECONHECIMENTO DE EXP RELACIONAL E ARITMETICA
            if (isRelacional(follow)) {
                expressaoRelacional(follow.concat(":then"));
                condicional(follow);
            } else if (isLogica(follow)) {
                expressaoLogica(follow);
                condicional(follow);
            } else {
                SintaticError("Esperado uma expressão relacional ou logica");
            }
            this.firstID_SemAtribuicao = false;
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no comando if", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
            condicional(follow);
        }
    }

    public void condicional(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals("then")) {
                parser.nextToken();
                if (parser.getCurrentToken().getLexema().equals("{")) {
                    //FALTA CONSTRUIR COMANDO <INSTRUÇÃO>
                    parser.nextToken();
                    instrucao("}");
                    //parser.nextToken();
                    if (parser.getCurrentToken().getLexema().equals("}")) {
                        parser.nextToken();
                        condicional2("}");

                    } else {
                        SintaticError("Era Esperado um }");
                    }
                } else {
                    SintaticError("Era Esperado'{'");
                }

            } else {
                follow = follow.concat(":{");
                SintaticError("Esperado um 'then'");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no comando if", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void condicional2(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals("else")) {
                parser.nextToken();
                if (parser.getCurrentToken().getLexema().equals("{")) {
                    //FALTA CONSTRUIR COMANDO <INSTRUÇÃO>
                    parser.nextToken();
                    instrucao("}");
                    if (parser.getCurrentToken().getLexema().equals("}")) {
                        parser.nextToken();
                    } else {
                        SintaticError("Era Esperado um }");
                    }
                } else {
                    SintaticError("Era Esperado'{'");
                }
            } else {
                //não foi encontrado o else, pula pro proximo token
                //parser.nextToken();
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no comando if", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

// ------------ FIM COMANDO IF ---------------------------------
// ------------ COMANDO WHILE ---------------------------------------
    public void comandoWhile(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals("while")) {
                parser.nextToken();
                if (parser.getCurrentToken().getLexema().equals("(")) {
                    parser.nextToken();
                    this.firstID_SemAtribuicao = true;
                    expressaoParada(")");
                    this.firstID_SemAtribuicao = false;
                    if (parser.getCurrentToken().getLexema().equals(")")) {
                        parser.nextToken();
                        if (parser.getCurrentToken().getLexema().equals("{")) {
                            parser.nextToken();
                            instrucao("}");
                            if (parser.getCurrentToken().getLexema().equals("}")) {
                                parser.nextToken();
                            } else {
                                SintaticError("Esperado }");
                            }
                        } else {
                            SintaticError("Esperado {");
                        }
                    } else {
                        SintaticError("Esperado )");
                    }
                } else {
                    SintaticError("Esperado (");
                }
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no comando while", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

// ------------ FIM COMANDO WHILE ---------------------------------------
// ------------ COMANDO FOR ---------------------------------------
    public void comandoFor(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals("for")) {
                parser.nextToken();
                if (parser.getCurrentToken().getLexema().equals("(")) {
                    parser.nextToken();
                    expressaoFor("}");
                    if (parser.getCurrentToken().getLexema().equals(";")) {
                        parser.nextToken();
                        this.firstID_SemAtribuicao = true;
                        expressaoParada(";");
                        this.firstID_SemAtribuicao = false;
                        if (parser.getCurrentToken().getLexema().equals(";")) {
                            parser.nextToken();
                            expressaoFor2(")");
                            if (parser.getCurrentToken().getLexema().equals(")")) {
                                parser.nextToken();
                                if (parser.getCurrentToken().getLexema().equals("{")) {
                                    parser.nextToken();
                                    instrucao("}");
                                    if (parser.getCurrentToken().getLexema().equals("}")) {
                                        parser.nextToken();
                                    } else {
                                        SintaticError("Esperado }");
                                    }
                                } else {

                                    SintaticError("Esperado {");

                                }
                            } else {
                                SintaticError("Esperado )");
                            }
                        } else {
                            SintaticError("Esperado ;");
                        }
                    } else {
                        SintaticError("Esperado ;");
                    }
                } else {
                    SintaticError("Esperado (");
                }
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no comando for", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void expressaoFor(String follow) {
        try {
            if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                //análise semântica -> verifica se a variável existe
                semanticExpressaoFor();
                //fim da análise semântica
                parser.nextToken();
                if (parser.getCurrentToken().getLexema().equals("=")) {
                    if (variavel.isConstante() && blocoMetodo == true && blocoParametros == false) { //verifica se é uma constante. Caso positivo exibe o erro.
                        SemanticError("Constante não pode ser atribuída");
                    }
                    parser.nextToken();
                    expressaoAritmetica("}");

                } else {
                    SintaticError("Esperado =");
                }
            } else {
                SintaticError("Esperado um identificador");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no comando for", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void semanticExpressaoFor() {
        this.variavel = tabelaSimbolos.contain(parser.getCurrentToken().getLexema(), classe, metodo);
        if (this.variavel == null) {
            SemanticError("Identificador não declarado");
            variavel = new EntryVar(false);
            variavel.setName(parser.getCurrentToken().getLexema());
            variavel.setDim(0);
            variavel.setType(null);
            //modifiquei, pois estava (!(variavel.getType().getName().equals(Symtable.INTEGER) || variavel.getType().getName().equals(Symtable.FLOAT)))
            //iria dar resultado logico diferente do esperado
        } else if (!(variavel.getType().getName().equals(Symtable.INTEGER)) || !(variavel.getType().getName().equals(Symtable.FLOAT))) {
            SemanticError("Deve ser do Tipo 'integer' ou 'float'");
        }
    }

    public void expressaoFor2(String follow) {
        try {
            if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                semanticExpressaoFor();
                parser.nextToken();
                expressaoFor2Aux("=");

            } else {
                SintaticError("Esperado um identificador");
            }

        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no comando for", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void expressaoFor2Aux(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals("=")) {
                if (variavel.isConstante() && blocoMetodo == true && blocoParametros == false) { //verifica se é uma constante. Caso positivo exibe o erro.
                    SemanticError("Constante não pode ser atribuída");
                }
                parser.nextToken();
                expressaoAritmetica(";");
            } else if (parser.getCurrentToken().getLexema().equals("++") || parser.getCurrentToken().getLexema().equals("--")) {
                parser.nextToken();
            } else {
                SintaticError("Esperado um operador '=',operador '++' ou '--'");
            }

        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    //TEM QUE MELHORAR ESSES METODOS DE IDENTIFICAÇÃO DE EXPRESSÕES
    public void expressaoParada(String follow) {
        try {
            if (isLogica(follow)) {
                expressaoLogica(follow);

            } else if (isRelacional(follow)) {
                expressaoRelacional(follow);

            } else {
                SintaticError("Esperado expressão logica ou relacional");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);

        }
    }
// ------------ FIM COMANDO FOR ---------------------------------------

//******************************************************************
    // ------------ DEFINICAO DE CONSTANTES ----------------------------
    public void definicaoConstante(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals("constants")) {
                parser.nextToken();
                if (parser.getCurrentToken().getLexema().equals("{")) {
                    parser.nextToken();
                    declaracaoConstante(follow.concat(":}"));
                    if (parser.getCurrentToken().getLexema().equals("}")) {
                        parser.nextToken();
                    } else {
                        SintaticError("Esperado }");
                    }
                } else {
                    SintaticError("Esperado {");
                }
            } else {
                SintaticError("Esperado constants");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no bloco 'constants'", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void declaracaoConstante(String follow) {
        if (checkTipo()) {
            //Análise Semântica
            variavel = new EntryVar(true);
            if (global == true) {
                tabelaSimbolos.getGlobais().add(variavel);
            } else if (classe != null) {
                classe.getVariaveis().add(variavel);
            }
            //fim da análise semântica
            declaracaoInicializado(follow.concat(":;:,"));
            declaracaoConstanteSame(follow);
        }
    }

    public void declaracaoConstanteSame(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals(",")) {
                parser.nextToken();
                atribuicao(follow.concat(":;"));
                declaracaoConstanteSame(follow);
            } else if (parser.getCurrentToken().getLexema().equals(";")) {
                parser.nextToken();
                declaracaoConstante(follow);
            } else {
                SintaticError("Esperado , ou ;");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no bloco 'constants'", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }

    }

    // ------------ FIM DEFINICAO DE CONSTANTES --------------------------
    // ------------ DECLARAÇÃO DE VARIAVEIS ------------------------------
    public void definicaoVariaveis(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals("variables")) {
                parser.nextToken();
                if (parser.getCurrentToken().getLexema().equals("{")) {
                    parser.nextToken();
                    declaracaoVariaveis(follow.concat(":}"));
                    if (parser.getCurrentToken().getLexema().equals("}")) {
                        parser.nextToken();
                    } else {
                        SintaticError("Esperado }");
                    }
                }
            } else {
                SintaticError("Esperado variables");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no bloco 'variables'", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void declaracaoVariaveis(String follow) {
        blocoDeclaracaoVariaveis =true;
        if (checkTipo()) {
            //Análise Semântica
            variavel = new EntryVar(false);
            if (global == true) {
                tabelaSimbolos.getGlobais().add(variavel);
            } else if (classe != null && this.blocoMetodo == false) {
                classe.getVariaveis().add(variavel);
            } else if (this.blocoMetodo == true) {
                metodo.getVariaveis().add(variavel);
            }
            //fim da análise semântica
            declaracao(follow);
            vetor(follow);
            inicializacaoVariavel(follow);
            
        }
        blocoDeclaracaoVariaveis =false;
    }

    public void inicializacaoVariavel(String follow) {
        try {
            //declaracaoInicializado(follow.concat(":;:,"));
            if (parser.getCurrentToken().getLexema().equals("=")) {
                parser.nextToken();
                /*if(parser.getCurrentToken().getLexema().equals("true")||
                 parser.getCurrentToken().getLexema().equals("true")){ //forcei aceitar 
                 parser.nextToken();
                 declaracaoVariaveisMesmoTipo(follow);
                 }
                 else{
                
                 valor(follow.concat(":,:;"));*/
                atribuicaoAux(follow);
                declaracaoVariaveisMesmoTipo(follow);
                //}
            } else if (parser.getCurrentToken().getLexema().equals(",")
                    || parser.getCurrentToken().getLexema().equals(";")) {
                declaracaoVariaveisMesmoTipo(follow);
            } else {
                SintaticError("Esperado '=', ',' ou ';'");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático  no bloco 'variables'", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void declaracaoVariaveisMesmoTipo(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals(",")) {
                parser.nextToken();
                if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                    semanticDeclaracaoMesmoTipo();
                    parser.nextToken();
                    vetor(follow);
                    inicializacaoVariavel(follow);
                } else {
                    SintaticError("Esperado Identificador");
                }
            } else if (parser.getCurrentToken().getLexema().equals(";")) {
                parser.nextToken();
                declaracaoVariaveis(follow);
            } else {
                SintaticError("Esperado , ou ;");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no bloco 'variables'", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void semanticDeclaracaoMesmoTipo() {

        //Análise semântica
        String lexema = parser.getCurrentToken().getLexema();
        EntryVar varAuxGlobal = null;
        EntryVar varAuxLocal = null;
        //EntryVar varParam = null;
        EntryVar varMethod = null;
        variavel = new EntryVar(lexema, variavel.getType()); //cria variável com mesmo tipo e identificação diferente
        if (this.blocoMetodo == true) { //declaracao dentro do bloco de métodos. Parametro e variables
            //varParam = metodo.getParam(lexema);
            varMethod = metodo.getVariable(lexema);
            if (varMethod == null) {
                //variavel.setName(lexema);
                //EntryVar varAux = new EntryVar(lexema, variavel.getType()); //cria variável com mesmo tipo e identificação diferente
                metodo.getParams().add(variavel); //add variavel como parâmetro do método
            } else {
                SemanticError("Identificador já utilizado");
            }
        } else if (this.blocoClasse == true && classe != null) { //dentro do bloco classe
            varAuxLocal = classe.getVariables(lexema);
            if (varAuxLocal == null) {
                //EntryVar varAux = new EntryVar(lexema, variavel.getType()); //cria variável com mesmo tipo e identificação diferente
                classe.getVariaveis().add(variavel); //add variavel como parâmetro do método
            } else {
                SemanticError("Identificador já utilizado");
            }
        } else if (this.global == true) { // dentro do bloco de variaveis globais
            varAuxGlobal = tabelaSimbolos.getVarGlobal(lexema);
            if (varAuxGlobal == null) {
                //EntryVar varAux = new EntryVar(lexema, variavel.getType()); //cria variável com mesmo tipo e identificação diferente
                tabelaSimbolos.getGlobais().add(variavel); //add variavel como parâmetro do método
            } else {
                SemanticError("Identificador já utilizado");
            }
        }
        //Fim da análise
    }

    // ------------ FIM DECLARAÇÃO DE VARIAVEIS ------------------------------
    // ------------ DECLARAÇÃO DE CLASSE ---------------------------------
    public void declaracaoClasse(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals("class")) {
                parser.nextToken();
                if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                    //Análise Semântica
                    String nome = parser.getCurrentToken().getLexema();
                    EntryClass classeAux = tabelaSimbolos.getClass(nome);
                    if (classeAux == null) { //se ainda não foi declarada
                        classe = new EntryClass(nome);
                        tabelaSimbolos.getMainTable().add(classe); //add classe na tabela de símbolos.
                        System.out.println("Add classe " + classe.getName());
                    } else {
                        classe = null;
                        SemanticError("Classe já declarada");
                    }
                    //Fim da Análise Semântica
                    parser.nextToken();
                    heranca(follow.concat(":}:{:constants:variables"));
                    if (parser.getCurrentToken().getLexema().equals("{")) {
                        parser.nextToken();
                        classBody(follow.concat(":}:constants:variables:class"));
                        if (parser.getCurrentToken().getLexema().equals("}")) {
                            parser.nextToken();
                        } else {
                            SintaticError("Esperado }");
                        }
                    } else {
                        follow.concat(":{:}:constants:variables");
                        SintaticError("Esperado {");
                    }
                } else {
                    follow.concat(":extends");
                    SintaticError("Esperado Identificador");
                }
            } else {
                SintaticError("Esperado class");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no bloco 'class'", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    //INSERI O RETORNO DO TIPO ENTRYCLASS PARA COLOCAR NA CLASSE FILHO
    public void heranca(String follow) {
        // EntryClass classePai = null;
        try {
            if (parser.getCurrentToken().getLexema().equals("extends")) {
                parser.nextToken();
                if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                    //Análise Semântica
                    EntryClass classePai = tabelaSimbolos.getClass(parser.getCurrentToken().getLexema());
                    if (classePai != null) {
                        if (classe != null) {
                            if (classePai.getName().equals(classe.getName())) {
                                SemanticError("Classe pai igual a classe filha");
                            } else {
                                classe.setParent(classePai);
                            }
                        }
                    } else {
                        SemanticError("Classe não declarada");
                    }
                    //Fim da Análise Semântica
                    parser.nextToken();
                } else {
                    SintaticError("Esperado Identificador");
                }
            } else if (!parser.getCurrentToken().getLexema().equals("{")) {
                SintaticError("Esperado extends");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático na definição de herança", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }

    }

    public void classBody(String follow) {
        declaracaoCampos(follow);
        declaracaoMetodos(follow);
    }

    public void declaracaoCampos(String follow) {
        definicaoConstante(follow);
        definicaoVariaveis(follow);

    }

    // ------------ FIM DECLARAÇÃO DE CLASSE --------------------------------
    // ------------ DECLARAÇÃO DE MÉTODO ------------------------------------
    public void declaracaoMetodos(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals("methods")) {
                parser.nextToken();
                if (parser.getCurrentToken().getLexema().equals("{")) {
                    parser.nextToken();
                    definicaoMetodoAux(follow.concat(":}"));
                    if (parser.getCurrentToken().getLexema().equals("}")) {
                        parser.nextToken();
                    } else {
                        SintaticError("Esperado }");
                    }
                } else {
                    SintaticError("Esperado {");
                }
            } else {
                SintaticError("Esperado methods");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático na declarção do metodo", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void definicaoMetodoAux(String follow) {
        if (checkTipo() || parser.getCurrentToken().getLexema().equals("void")) { //é o primeiro de "definição método"
            //Análise Semântica
            this.blocoMetodo = true;
            metodo = new EntryMethod();
            if (classe != null) {
                classe.getMetodos().add(metodo);
            }
            //Fim análise semântica
            definicaoMetodo(follow);
            definicaoMetodoAux(follow);
        }

    }

    public void definicaoMetodo(String follow) {
        metodoHeader("(");
        try {
            if (parser.getCurrentToken().getLexema().equals("(")) {
                this.blocoParametros = true;
                parser.nextToken();
                declaracaoParametrosMetodo(")");
                //Análise semântica --> verifica se o método main é empty.
                if ((!metodo.getParams().isEmpty()) && metodo.isMain()) {
                    tabelaSimbolos.setMain(false);
                    metodo.setMain(false);
                    SemanticError("Método 'main' não dever ter parâmetros");
                }
                //verifica Sobrecarga
                semanticSobrecarga();
                //fim da análise semântica
                if (parser.getCurrentToken().getLexema().equals(")")) {

                    parser.nextToken();
                    metodoBody(follow);
                } else {
                    //follow="{:)";
                    SintaticError("Esperado )");
                }
            } else {
                SintaticError("Esperado (");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático na definição do metodo", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void semanticSobrecarga() {
        //verifica sobrecarga
        boolean sobrecargaError = false;
        ArrayList<EntryMethod> methods = classe.getMethods(metodo.getName());
        for (EntryMethod method : methods) {
            if (!method.equals(metodo)) {
                if (method.getParams().size() == metodo.getParams().size()) {
                    if (method.getParams().isEmpty()) {
                        sobrecargaError = true;
                        break;
                    }
                    ArrayList<EntryVar> paramsMethod = (ArrayList<EntryVar>) metodo.getParams();
                    ArrayList<EntryVar> paramsMethodAux = (ArrayList<EntryVar>) method.getParams();
                    for (int i = 0; i < paramsMethod.size(); i++) {
                        String tipoParam = paramsMethod.get(i).getType().getName();
                        String tipoParamAux = paramsMethodAux.get(i).getType().getName();
                        if (tipoParam.equals(tipoParamAux)) {
                            sobrecargaError = true;
                        } else {
                            sobrecargaError = false;
                            break;
                        }
                    }
                    if (sobrecargaError) {
                        break;
                    }
                }
            }
        }
        if (sobrecargaError) {
            SemanticError("Método já declarado.");
        }

    }

    public void metodoHeader(String follow) {
        try {
            if (checkTipo()) {
                tipo(follow);
                if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR || parser.getCurrentToken().getLexema().equals("main")) {
                    //Análise semântica
                    String lexema = parser.getCurrentToken().getLexema();

                    //EntryVar varAux = classe.getVariables(lexema);
                    EntryVar varAux = tabelaSimbolos.contain(lexema, classe, metodo);
                    if (varAux == null) {
                        //verifica se é declaração de método principal
                        if (lexema.equals("main")) {
                            if (tabelaSimbolos.hasMain()) { //caso já exista um método main declarado é emitido o erro.
                                SemanticError("Já foi declarado um método principal");
                            } else {
                                if (metodo.getType().getName().equals("void")) {
                                    metodo.setMain(true);
                                    tabelaSimbolos.setMain(true);
                                } else {
                                    SemanticError("O retorno do método 'main' deve ser 'void'");
                                }

                            }
                        }
                        this.metodo.setName(parser.getCurrentToken().getLexema()); //Insere o nome do método.
                    } else {
                        SemanticError("Método não pode ter mesma identificação que variável");
                    }
                    //Fim da análise semântica
                    parser.nextToken();
                } else {
                    SintaticError("Esperado Identificador");
                }
            } else if (parser.getCurrentToken().getLexema().equals("void")) {
                parser.nextToken();
                if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                    parser.nextToken();
                } else {
                    SintaticError("Esperado Identificador");
                }
            } else {
                SintaticError("Um Método deve haver retorno");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático na declaração de parametros", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void declaracaoParametrosMetodo(String follow) {
        try {
            this.variavel = new EntryVar(false); //análise semântica
            if (checkTipo()) {
                declaracao(follow.concat(":,:;"));
                metodo.getParams().add(variavel); //análise semântica
                declaracaoParametros(follow);
            } else if (parser.getCurrentToken().getLexema().equals("empty")) {
                parser.nextToken();
            } else {
                SintaticError("Esperado um empty,interger,float,string,boolean,char ou identificador");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintáticona declaração de parametros", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void declaracaoParametros(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals(",")) {
                parser.nextToken();
                if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                    semanticDeclaracaoParametro();
                    parser.nextToken();
                    declaracaoParametros(follow);
                } else {
                    SintaticError("Esperado um identificador");
                }
            } else if (parser.getCurrentToken().getLexema().equals(";")) {
                parser.nextToken();
                this.variavel = new EntryVar(false); //análise semântica
                declaracao(follow.concat(":,:;"));
                metodo.getParams().add(variavel); //análise semântica
                declaracaoParametros(follow);
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático na declaração de parametros", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    private void semanticDeclaracaoParametro() {
        //Análise semântica
        String lexema = parser.getCurrentToken().getLexema();
        /* EntryVar varAuxGlobal = tabelaSimbolos.getVarGlobal(lexema);
         EntryVar varAuxLocal = null;*/
        //EntryVar varParam = metodo.getParam(lexema);
        EntryVar varParam = metodo.getParam(lexema);
        /*if (classe != null) {
         varAuxLocal = classe.getVariables(lexema);
         }*/
        if (varParam == null) { // não pode ter o mesmo nome de variaveis global e local.
            EntryVar varAux = new EntryVar(parser.getCurrentToken().getLexema(), variavel.getType()); //cria variável com mesmo tipo e identificação diferente
            metodo.getParams().add(varAux); //add variavel como parâmetro do método
        } else {
            SemanticError("Identificador já utilizado");
        }
        //Fim da análise
    }

    public void metodoBody(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals("{")) {
                parser.nextToken();
                definicaoVariaveis(follow.concat(":return"));
                this.blocoParametros = false;
                instrucaoMetodo("}");
                if (parser.getCurrentToken().getLexema().equals("}")) {
                    parser.nextToken();
                    this.blocoMetodo = false;
                } else {
                    SintaticError("Esperado }");
                }
            } else {
                SintaticError("Esperado {");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no corpo do metodo", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void instrucaoMetodo(String follow) {
        try {
            //   if (checkInstrucaoGeral()) {
            instrucao(follow.concat(":return"));
            if (parser.getCurrentToken().getLexema().equals("return")) {
                parser.nextToken();
                if (parser.getCurrentToken().getLexema().equals("(")) {
                    parser.nextToken();
                    if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR || parser.getCurrentToken().getTipo() == Token.PALAVRA_RESERVADA) {
                        semanticCheckReturn();//análise semântica
                        parser.nextToken();
                    } else {//caso vazio verifica se o retorno do método é void.
                        if (metodo.getType() != null) {
                            if (!metodo.getType().getName().equals("void")) {
                                SemanticError("Tipo de retorno não compatível");
                            }
                        } else {
                            SemanticError("Tipo de retorno não compatível");
                        }
                    }
                    if (parser.getCurrentToken().getLexema().equals(")")) {
                        parser.nextToken();
                        if (parser.getCurrentToken().getLexema().endsWith(";")) {
                            parser.nextToken();
                        } else {
                            SintaticError("Esperado ;");
                        }
                    } else {
                        SintaticError("Esperado )");
                    }

                } else {
                    SintaticError("Esperado (");
                }
            } else {
                SintaticError("Esperado return");
            }
            /* } else {
             SintaticError("Esperado uma intrução");
             }*/
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático no corpo do metod", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    private void semanticCheckReturn() {
        //Análise semântica
        String lexema = parser.getCurrentToken().getLexema();
        EntryVar varAuxGlobal = tabelaSimbolos.getVarGlobal(lexema);
        EntryVar var = tabelaSimbolos.contain(lexema, classe, metodo);
        EntryVar varAuxLocal = null;
        EntryVar varParam = null;
        EntryVar varMethod = null;
        if (this.blocoMetodo == true) {
            varParam = metodo.getParam(lexema);
            varMethod = metodo.getVariable(lexema);
        }
        if (classe != null) {
            varAuxLocal = classe.getVariables(lexema);
        }
        //if (varAuxGlobal == null && varAuxLocal == null && varParam == null && varMethod == null) { // se não foi declarada
        if (!lexema.equals("empty")) {
            if (var == null) {
                SemanticError("Identificador em return não declarado");
            } else if (metodo.getType() == null) {
                SemanticError("Tipo de retorno não compatível");
            } else if (!metodo.getType().getName().equals(var.getType().getName())) {
                SemanticError("Tipo de retorno não compatível");
            }
        }
        //Fim da análise
    }

    public void chamadaMetodo(String follow) {
        try {
            this.construtor = false;
            this.blocoChamadaMetodo = true;
            if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                //Análise Semântica
                String lexema = parser.getCurrentToken().getLexema();
                EntryVar var = tabelaSimbolos.contain(lexema, classe, metodo);
                ArrayList<EntryMethod> methods = classe.getMethods(lexema);
                this.metodoChamado = new EntryMethod(lexema, null, new ArrayList<>());
                //metodoChamado.setParams(new ArrayList<>());
                if (var != null) { //construtor
                    this.construtor = true;
                } else if (!methods.isEmpty()) {

                } else {
                    SemanticError("Identificador não declarado");
                }
                //Fim da análise semântica
                parser.nextToken();
                chamadaMetodoAux("(", var);
            } else {
                SintaticError("Esperado um identificador");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático na chamada do metodo", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void chamadaMetodoAux(String follow, EntryVar var) {
        try {

            if (parser.getCurrentToken().getLexema().equals("(")) {
                parser.nextToken();

                chamadaMetodoParam(")");
                if (parser.getCurrentToken().getLexema().equals(")")) {
                    parser.nextToken();
                } else {
                    SintaticError("Esperado um ')'");
                }

            } else if (parser.getCurrentToken().getLexema().equals(".")) {
                parser.nextToken();
                if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                    //análise semântica
                    if (var != null) {
                        EntryClass classAux = var.getType();
                        ArrayList<EntryMethod> listMethod = classAux.getMethods(parser.getCurrentToken().getLexema());
                        if (listMethod.isEmpty()) {
                            SemanticError("Método não declarado");
                        } else {

                        }
                    }
                    else{
                      //  classe.getMethods(follow);
                    }
                    parser.nextToken();
                    if (parser.getCurrentToken().getLexema().equals("(")) {
                        parser.nextToken();
                        chamadaMetodoParam(")");
                        if (parser.getCurrentToken().getLexema().equals(")")) {
                            parser.nextToken();

                        } else {
                            SintaticError("Esperado um )");
                        }
                    } else {
                        SintaticError("Esperado um (");
                    }

                } else {
                    SintaticError("Esperado um Identificador");
                }

            } else {
                SintaticError("Esperado um '(' ou um '.'");

            }

        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático na chamada do metodo", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }

    }

    public void chamadaMetodoParam(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals(")")) {
                //nao faz nada, pois é (), sem nada dentro do parenteses
                if (this.construtor) {
                } else {
                    //metodoChamado.setParams(new ArrayList<>());
                    sematicCompareMetodos();
                }
            } else if (checkValor(follow)) {
                if (this.construtor) { // se é um construtor exibe erro.
                    SemanticError("Construtor não deve ter parâmetros");
                }
                valor(follow);
                while (true) {
                    if (parser.getCurrentToken().getLexema().equals(",")) {
                        parser.nextToken();
                        valor(")");
                    } else if (parser.getCurrentToken().getLexema().equals(")")) {
                        sematicCompareMetodos();
                        break;
                    } else {
                        SintaticError("Esperado Identificador, String, Numero ou Caractere");
                        break;
                    }
                }
            } else {
                SintaticError("Esperado Identificador, String, Numero, Caractere ou nenhuma cadeia");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático nos parametros da chamada do metodo", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }

    }

    public void sematicCompareMetodos() {
        //verifica metodos
        boolean match = false;
        ArrayList<EntryMethod> methods = classe.getMethods(metodoChamado.getName());//capturas os metodos com o mesmo nome
        for (EntryMethod method : methods) { //itera sobre os metodos com o mesmo nome para veirifcar se acerta algum
            if (!method.equals(metodoChamado)) {
                if (method.getParams().size() == metodoChamado.getParams().size()) { //verifica qual tem a mesma quantidade de parametros
                    if (method.getParams().isEmpty()) { //se os dois não tem parâmetros
                        match = true;
                        break;
                    }
                    ArrayList<EntryVar> paramsMethod = (ArrayList<EntryVar>) metodoChamado.getParams();
                    ArrayList<EntryVar> paramsMethodAux = (ArrayList<EntryVar>) method.getParams();
                    for (int i = 0; i < paramsMethod.size(); i++) { //comparação dos parâmetros
                        String tipoParam = paramsMethod.get(i).getType().getName();
                        String tipoParamAux = paramsMethodAux.get(i).getType().getName();
                        if (tipoParam.equals(tipoParamAux)) {
                            match = true;
                        } else {
                            match = false;
                            break;
                        }
                    }
                    if (match) {
                        break;
                    }
                }
            }
        }
        if (!match) {
            SemanticError("Método não declarado.");
        }

    }

    public boolean checkTipo() {
        if (parser.getCurrentToken().getLexema().equals("integer")
                || parser.getCurrentToken().getLexema().equals("string")
                || parser.getCurrentToken().getLexema().equals("float")
                || parser.getCurrentToken().getLexema().equals("boolean")
                || parser.getCurrentToken().getLexema().equals("char")
                || parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
            return true;
        } else {
            return false;
        }
    }
    // ------------ FIM DECLARAÇÃO DE MÉTODO --------------------------------
    // ------------ INSTANCIA DE OBJETO --------------------------------
    //testes para passar --> float a ou float a(2) ou float a (2,a)

    public void inicializaObjeto(String follow) {
        //declaracao("(");
        if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
            parser.nextToken();
            //inicializaObjetoAux(follow);
            if (parser.getCurrentToken().getLexema().equals("(")) {
                parser.nextToken();
                valor(",:)");
                inicializaObjetoAux(follow);
                if (parser.getCurrentToken().getLexema().equals(")")) {
                    parser.nextToken();
                } else {
                    try {
                        SintaticError("Esperado )");
                    } catch (ASException ex) {
                        TableLexema.getInstance().addErroSintatico("Erro Sintático na incialização do objeto", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
                        System.err.println(ex.getMessage());
                        consumeUntil(follow);
                    }
                }
            } else {
                try {
                    SintaticError("Esperado (");
                } catch (ASException ex) {
                    TableLexema.getInstance().addErroSintatico("Erro Sintático na incialização do objeto", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
                    System.err.println(ex.getMessage());
                    consumeUntil(follow);
                }
            }
        } else {
            try {
                SintaticError("Esperado um Identificador");
            } catch (ASException ex) {
                TableLexema.getInstance().addErroSintatico("Erro Sintático na incialização do objeto", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
                System.err.println(ex.getMessage());
                consumeUntil(follow);
            }
        }
    }

    public void inicializaObjetoAux(String follow) {
        if (parser.getCurrentToken().getLexema().equals(",")) {
            parser.nextToken();
            valor(",:)");
            inicializaObjetoAux(follow);
        }
    }

    // ------------ FIM INSTANCIA DE OBJETO ----------------------------
    public void atributoObjeto(String follow) {
        try {
            if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                parser.nextToken();
                if (parser.getCurrentToken().getLexema().equals(".")) {
                    parser.nextToken();
                    if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                        parser.nextToken();
                    } else {
                        SintaticError("Esperado Identificador");
                    }
                } else {
                    SintaticError("Esperado .");
                }
            } else {
                SintaticError("Esperado Identificador");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public boolean checkValor(String follow) {
        boolean teste = false;//true, se for string, num, carac, id. False qualquer outro tipo
        if (parser.getCurrentToken().getTipo() == Token.STRING || parser.getCurrentToken().getTipo() == Token.NUMERO || parser.getCurrentToken().getTipo() == Token.CARACTERE || parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
            teste = true;
        }

        return teste;
    }

    public void valor(String follow) {

        try {
            if (parser.getCurrentToken().getTipo() == Token.STRING) {
                //análise semântica
                if (this.firstID_SemAtribuicao == true) { // primeira variável da expressão pra ser declarada.
                    variavel = new EntryVar(parser.getCurrentToken().getLexema(), tabelaSimbolos.getClass(Symtable.STRING), 0, true); //cria variavel do tipo float para ser comparada
                    this.firstID_SemAtribuicao = false;
                }
                if (this.blocoChamadaMetodo) {
                    EntryVar varStr = new EntryVar(parser.getCurrentToken().getLexema(), tabelaSimbolos.getClass(Symtable.STRING), 0, false);
                    metodoChamado.getParams().add(varStr);
                } else {
                    if(variavel.getType()!=null){
                        if (!variavel.getType().getName().equals(Symtable.STRING)) {
                            SemanticError("Atribuicao com Tipos diferentes");
                        }
                    }
                    else{
                        SemanticError("Atribuicao com Tipos diferentes");
                    }
                }
                //fim da análise semântica
                parser.nextToken();
            } else if (parser.getCurrentToken().getTipo() == Token.NUMERO) {
                if (this.blocoChamadaMetodo) {
                    String numero = parser.getCurrentToken().getLexema();
                    if (numero.contains(".")) {
                        EntryVar varNum = new EntryVar(numero, tabelaSimbolos.getClass(Symtable.FLOAT), 0, false);
                        metodoChamado.getParams().add(varNum);
                    } else {
                        EntryVar varNum = new EntryVar(numero, tabelaSimbolos.getClass(Symtable.INTEGER), 0, false);
                        metodoChamado.getParams().add(varNum);
                    }
                }
                parser.nextToken();
            } else if (parser.getCurrentToken().getTipo() == Token.CARACTERE) {
                //análise semântica
                if (this.firstID_SemAtribuicao == true) { // primeira variável da expressão pra ser declarada.
                    variavel = new EntryVar(parser.getCurrentToken().getLexema(), tabelaSimbolos.getClass(Symtable.CHAR), 0, true); //cria variavel do tipo float para ser comparada
                    this.firstID_SemAtribuicao = false;
                }
                if (this.blocoChamadaMetodo) {
                    EntryVar varChar = new EntryVar(parser.getCurrentToken().getLexema(), tabelaSimbolos.getClass(Symtable.CHAR), 0, false);
                    metodoChamado.getParams().add(varChar);
                } else {
                    if (!variavel.getType().getName().equals(Symtable.CHAR)) {
                        SemanticError("Atribuicao com Tipos diferentes");
                    }
                }
                //fim da análise semântica
                parser.nextToken();
            } else if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                EntryVar varAux = tabelaSimbolos.contain(parser.getCurrentToken().getLexema(), classe, metodo);
                if (!this.blocoChamadaMetodo) {
                    //EntryVar varAux = tabelaSimbolos.contain(parser.getCurrentToken().getLexema(), classe, metodo);
                    if (varAux == null) {
                        SemanticError("Identificador não declarado");
                    } else if (variavel.getType() == null) {
                        SemanticError("Identificador não é do mesmo tipo");
                    } else if (!varAux.getType().getName().equals(variavel.getType().getName())) {
                        SemanticError("Identificador não é do mesmo tipo");
                    }
                    
                } else {
                    //EntryVar varAux = tabelaSimbolos.contain(parser.getCurrentToken().getLexema(), classe, metodo);
                    if (varAux == null) {
                        SemanticError("Identificador não declarado");
                    } else {
                        metodoChamado.getParams().add(varAux);
                    }

                }
                controleDimensaoVetor = varAux.getDim();
                parser.nextToken();
                //adicionei esse trecho de codigo aqui para atualizar os valores de controledimensaovetor e controleacessovetor
                //pois 
                vetor(follow.concat(":*:/:+:-:)"));
                    //controle de dimensão no acesso à vetores
                        if(controleAcessoVetor!=controleDimensaoVetor){
                        SemanticError("Dimensão da variavel acessada difere da que foi declarada");}
            } else {
                SintaticError("Esperado String, Número, Caractere ou Identificador");

            }

        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }

    }

    //First(Tipo)={}
    public void tipo(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals("integer")) {
                semanticTipo();
                parser.nextToken();
            } else if (parser.getCurrentToken().getLexema().equals("string")) {
                semanticTipo();
                parser.nextToken();
            } else if (parser.getCurrentToken().getLexema().equals("float")) {
                semanticTipo();
                parser.nextToken();
            } else if (parser.getCurrentToken().getLexema().equals("boolean")) {
                semanticTipo();
                parser.nextToken();
            } else if (parser.getCurrentToken().getLexema().equals("char")) {
                semanticTipo();
                parser.nextToken();
            } else if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                //Análise Semântica
                EntryClass classeAux = tabelaSimbolos.getClass(parser.getCurrentToken().getLexema());
                if (this.blocoMetodo == true && this.blocoParametros == false) {
                    if (parser.getCurrentToken().getLexema().equals("void") && metodo != null) {
                        EntryClass voidClass = new EntryClass("void");
                        metodo.setType(voidClass);
                    } else if (classeAux != null) {
                        metodo.setType(classeAux);
                    } else {
                        SemanticError("Tipo inválido");
                    }
                } else if (classeAux != null) {
                    variavel.setType(classeAux);
                } else {
                    SemanticError("Tipo inválido");
                }
                //fim da análise semântica
                parser.nextToken();
            } else {
                SintaticError("Esperado integer, string, float, boolean ou Identificador");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    private void semanticTipo() {
        if (this.blocoMetodo == true && this.blocoParametros == false) {
            metodo.setType(tabelaSimbolos.getClass(parser.getCurrentToken().getLexema()));
        } else {
            variavel.setType(tabelaSimbolos.getClass(parser.getCurrentToken().getLexema()));
        }
    }

    public void inc_dec(String follow) {
        if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
            variavel = tabelaSimbolos.contain(parser.getCurrentToken().getLexema(), classe, metodo);
            if (variavel == null) {
            SemanticError("Identificador não declarado");
            variavel = new EntryVar(false);
            variavel.setName(parser.getCurrentToken().getLexema());
            variavel.setDim(0);
            variavel.setType(null);
            
        } else if (!(variavel.getType().getName().equals(Symtable.INTEGER) || variavel.getType().getName().equals(Symtable.FLOAT))) {
            SemanticError("Deve ser do Tipo 'integer' ou 'float'");
        }
            
            
            
            parser.nextToken();
            op(follow);
        }
    }

    public void op(String follow) {
        try {
            if (parser.getCurrentToken().getLexema().equals("++")) {
                parser.nextToken();
            } else if (parser.getCurrentToken().getLexema().equals("--")) {
                parser.nextToken();
            } else {
                SintaticError("Esperado ++ ou --");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático na formação do operador", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void declaracao(String follow) {
        tipo(follow);
        try {
            if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                semanticDeclaracao();
                parser.nextToken();
            } else {
                SintaticError("Esperado Identificador");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    private void semanticDeclaracao() {
        //Análise semântica
        String lexema = parser.getCurrentToken().getLexema();
        EntryVar varAuxGlobal = null;
        EntryVar varAuxLocal = null;
        //EntryVar varParam = null;
        EntryVar varMethod = null;
        if (this.blocoMetodo == true) { //declaracao dentro do bloco de métodos. Parametro e variables
            //varParam = metodo.getParam(lexema);
            varMethod = metodo.getVariable(lexema);
            if (varMethod == null) {
                variavel.setName(lexema);
            } else {
                SemanticError("Identificador já utilizado");
            }
        } else if (this.blocoClasse == true && classe != null) { //dentro do bloco classe
            varAuxLocal = classe.getVariables(lexema);
            if (varAuxLocal == null) {
                variavel.setName(lexema);
            } else {
                SemanticError("Identificador já utilizado");
            }
        } else if (this.global == true) { // dentro do bloco de variaveis globais
            varAuxGlobal = tabelaSimbolos.getVarGlobal(lexema);
            if (varAuxGlobal == null) {
                variavel.setName(lexema);
            } else {
                SemanticError("Identificador já utilizado");
            }
        }
        //Fim da análise
    }

    public void declaracaoInicializado(String follow) {
        tipo(follow);
        atribuicao(follow.concat(":;"));
    }

    public void atribuicao(String follow) {
        try {
            if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                //Análise semântica. 
                if (blocoMetodo == true && blocoParametros == false) {
                    //Instrução Geral
                    this.variavel = tabelaSimbolos.contain(parser.getCurrentToken().getLexema(), classe, metodo);
                    if (this.variavel == null) {
                        SemanticError("Identificador não declarado");
                        variavel = new EntryVar(false);
                        variavel.setName(parser.getCurrentToken().getLexema());
                        variavel.setDim(0);
                        variavel.setType(null);
                    }
                } else if (this.global == true) { //está no bloco de constantes classe.
                    String lexema = parser.getCurrentToken().getLexema();
                    EntryVar varAux = tabelaSimbolos.getVarGlobal(lexema);
                    if (varAux == null) {
                        variavel.setName(lexema);
                    } else {
                        SemanticError("Identificador já utilizado");
                    }
                } else if (blocoClasse == true) {
                    String lexema = parser.getCurrentToken().getLexema();
                    EntryVar varAux = classe.getVariables(lexema);
                    if (varAux == null) {
                        variavel.setName(lexema);
                    } else {
                        SemanticError("Identificador já utilizado");
                    }
                }
                //fim da análise semântica
                parser.nextToken();
                if (parser.getCurrentToken().getLexema().equals("=")) {
                    //análise semântica -> atribuição de constante no corpo do método
                    if (variavel.isConstante() && blocoMetodo == true && blocoParametros == false) { //verifica se é uma constante. Caso positivo exibe o erro.
                        SemanticError("Constante não pode ser atribuída");
                    }
                    //fim da análise semântica
                    parser.nextToken();
                    //verificar se é expressão aritmética, valor ou expressão lógica
                    atribuicaoAux(follow);
                } else {
                    SintaticError("Esperado =");
                }
            } else {
                SintaticError("Esperado Identificador");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático na atribuição", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }

    }

    public void atribuicaoAux(String follow) {
        try {
            if (parser.getCurrentToken().getTipo() == Token.STRING
                    || parser.getCurrentToken().getTipo() == Token.CARACTERE) {
                valor(follow);
            } else if (parser.getCurrentToken().getLexema().equals("false")
                    || parser.getCurrentToken().getLexema().equals("true")) {
                if (!variavel.getType().getName().equals(Symtable.BOOLEAN)) {
                    SemanticError("Atribuicao com Tipos diferentes");
                }
                expressaoLogica(follow);
            } else if (parser.getCurrentToken().getTipo() == Token.NUMERO) {
                expressaoAritmetica(follow);
            } else if (parser.getCurrentToken().getTipo() == Token.IDENTIFICADOR) {
                Token tokenAux;
                int index = listToken.indexOf(parser.getCurrentToken());
                index++;
                tokenAux = listToken.get(index);
                if (tokenAux.getLexema().equals("&&")
                        || tokenAux.getLexema().equals("||")) {
                    expressaoLogica(follow);
                } else if (tokenAux.getLexema().equals("+")
                        || tokenAux.getLexema().equals("-")
                        || tokenAux.getLexema().equals("*")
                        || tokenAux.getLexema().equals("/")) {
                    expressaoAritmetica(follow);
                } else if (tokenAux.getLexema().equals(")")) {
                    index++;
                    tokenAux = listToken.get(index);
                    if (tokenAux.getLexema().equals("&&")
                            || tokenAux.getLexema().equals("||")) {
                        expressaoLogica(follow);
                    } else {
                        expressaoAritmetica(follow);
                    }
                } else if (tokenAux.getLexema().equals("[")) {
                    vetorDecisao(follow);
                    ///valor(follow);
                    //SintaticError("Esperado atribuição");
                } else {
                    valor(follow);
                    //SintaticError("Esperado atribuição");
                }
            } else if (parser.getCurrentToken().getLexema().equals("(")) {
                logicaOrAritmetica(follow);
            } else {
                SintaticError("Esperado atribuição");
            }
        } catch (ASException ex) {
            TableLexema.getInstance().addErroSintatico("Erro Sintático", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), ex.getMessage());
            System.err.println(ex.getMessage());
            consumeUntil(follow);
        }
    }

    public void vetorDecisao(String follow) throws ASException {
        Token tokenAux;
        int size = TableLexema.getInstance().getArray().size();
        int indiceCurrent = parser.getCurrentToken().getIndice();
        while ((indiceCurrent < size) && !listToken.get(indiceCurrent).getLexema().equals("]")) {
            indiceCurrent++;
        }
        if(indiceCurrent==size){
            SintaticError("Esperado ]");
        }
        indiceCurrent++;
        tokenAux = listToken.get(indiceCurrent);
        if (tokenAux.getLexema().equals("&&")
                || tokenAux.getLexema().equals("||")) {
            expressaoLogica(follow);
        } else if (tokenAux.getLexema().equals("+")
                || tokenAux.getLexema().equals("-")
                || tokenAux.getLexema().equals("*")
                || tokenAux.getLexema().equals("/")) {
            expressaoAritmetica(follow);
        }
        else{
           expressaoAritmetica(follow);
        }

    }

    public void logicaOrAritmetica(String follow) {
        Token tokenAux;
        int index = listToken.indexOf(parser.getCurrentToken());
        index++;
        tokenAux = listToken.get(index);
        if (tokenAux.getLexema().equals("false")
                || tokenAux.getLexema().equals("true")) {
            expressaoLogica(follow);
        } else if (tokenAux.getTipo() == Token.NUMERO) {
            expressaoAritmetica(follow);
        } else if (tokenAux.getTipo() == Token.IDENTIFICADOR) {
            index++;
            tokenAux = listToken.get(index);
            if (tokenAux.getLexema().equals("&&")
                    || tokenAux.getLexema().equals("||")) {
                expressaoLogica(follow);
            } else if (tokenAux.getLexema().equals("+")
                    || tokenAux.getLexema().equals("-")
                    || tokenAux.getLexema().equals("*")
                    || tokenAux.getLexema().equals("/")) {
                expressaoAritmetica(follow);
            } else if (tokenAux.getLexema().equals(")")) {
                index++;
                tokenAux = listToken.get(index);
                if (tokenAux.getLexema().equals("&&")
                        || tokenAux.getLexema().equals("||")) {
                    expressaoLogica(follow);
                } else {
                    expressaoAritmetica(follow);
                }
            } else {
                //manda para aritmética
            }
        }
    }
    //--------------------------------------------------------------------------

    public void SintaticError(String message) throws ASException {
        throw new ASException(message);
    }

    //comsumeUntil deve ser consertado para receber uma lista de string;
    public void consumeUntil(String str) { //Função que consome a cadeia até encontrar o "próximo".
        String[] sincronizadores;
        //str = "{:;";
        sincronizadores = str.split(":");
        ArrayList<String> sinc = new ArrayList<>(Arrays.asList(sincronizadores));
        //while (!parser.getCurrentToken().getLexema().equals(s)) {
        while (!sinc.contains(parser.getCurrentToken().getLexema())) {
            if (parser.getCurrentToken().getLexema().equals("EOF")) {
                return;
            }
            parser.nextToken();
        }
    }

    private void SemanticError(String erroMsg) {
        TableLexema.getInstance().addErroSintatico("Erro Semântico", parser.getCurrentToken().getLinha(), parser.getCurrentToken().getColuna(), erroMsg);
        System.err.println(erroMsg);
    }
}
